const { getManager, createConnections}  = require("typeorm");
const path = require('path');
const dataPath = path.join(__dirname,'..','..','..','conf/hako.json');
const { database } = require(dataPath);
const models = require('./models.js');

const findObjectID = function(object) {
    var objectID;
    if(typeof object == 'object') {
        if(object.id) {
            objectID = object.id;
        } else {
            throw new Error('object id not found');
        }
    } else if (object && typeof object == 'string') {
        objectID = object;
    } else {
        throw new Error('object id not found');
    }
    return objectID;
}

const removeIvalidKey = function(object) {
    Object.keys(object).forEach(key=> {
        if(!object[key] && object[key] !== 0) {
            delete object[key]
        }
    })
    return object;
}

const Connector = function() {
    createConnections(database).then(async ()=> {
        this.queryRunner = getManager('mysql');
    })
}

Connector.prototype.query = function(models, form) {

    if (!models) {
        throw new Error("models not found");
    }

    if(typeof models == 'string') {
        models = [models];
    } else if(!Array.isArray(models)){
        throw new Error('models can be string or array');
    }

    form = Object.assign({},{
        fields: [],
        start: 0,
        conditions: [],
        params: [],
        sort: '',
        limit: -1,
        group: ''
    },form);

    var querySQL = "select " + (form.fields.length ? form.fields.join(",") : "*") + " from " + models.join(" ") + (form.conditions.length ? (" where (" + form.conditions.join(") and (") + ")"  ) : "");

    if(form.group) {
        querySQL += " group by " + form.group;
    }

    if (form.limit < 0) {
        if (form.sort) {
            querySQL += " order by " + form.sort;
        }
    } else {
        if (form.sort) {
            querySQL += " order by " + form.sort + " limit ?,?";
        } else {
            querySQL += " limit ?,?";
        }
    }

    var countSQL = "select count(*) as count from " + models.join(" ") + (form.conditions.length ? (" where (" + form.conditions.join(") and (") + ")"  ) : "");


    form.params = form.params.filter(function(param) {
        return param != undefined;
    })

    return this.queryRunner.query(querySQL,form.params.concat(form.limit < 0 ? [] : [form.start, form.limit])).then(async (records)=> {
        let result = {};
        result.list = records;
        if(form.mapper) {
            result.list = records.map(form.mapper);
        } 
        if(form.limit < 0) {
            result.total = result.list.length;
        } else {
            let records = await this.queryRunner.query(countSQL,form.params);
            result.total = records[0].count
        }
        return result;
    })
}

Connector.prototype.execute = function(sql,params) {
    if(!Array.isArray(params)) {
        throw new Error("params must be array")
    }
    return this.queryRunner.query(sql, params)
}

Connector.prototype.find = function(model, id) {
    var lab = this;

    var condition = {};

    if (!id) {
        throw new Error("id not found");
    }
    if (!model || typeof model != 'string' || !models[model]) {
        throw new Error("model " + model +  " not found");
    }

    if(typeof id == 'string') {
        condition.id = id;
    } else if (typeof id == 'object' && id.id) {
        condition.id = id.id;
    } else {
        throw new Error('id format error');
    }

    return lab.queryRunner.find(model,condition).then(result=> {
        if(result.length) {
            return result[result.length-1];
        } else {
            return;
        }
    })
}

Connector.prototype.save = function(model, object) {
    var lab = this;
    if(!model || typeof model != 'string' || !models[model]) {
        throw new Error("model not found");
    }

    if(!(object && typeof object == 'object')) {
        throw new Error("object not valid");
    }
    if(object.id) {
        return lab.find(model,object.id).then(existing_object=> {
            if(existing_object) {
                return lab.update(model,Object.assign({},existing_object,object))
            } else {
                return lab.create(model,object)
            }
        })
    } else {
        return lab.create(model,object)
    }
}

Connector.prototype.update = function(model, object) {
    var lab = this;
    if (!model || typeof model != 'string' || !models[model]) {
        throw new Error("model " + model +  " not found");
    }
    var objectID = findObjectID(object);
    return lab.find(model, objectID).then(cached=> {
        if(cached) {
            let id = object.id;
            return lab.queryRunner.update(model,id,removeIvalidKey(Object.assign({},cached,object))).then(()=> {
                return removeIvalidKey(Object.assign({},cached,object))
            })
        } else {
            throw new Error(`object not found and id = ${objectID}`);
        }
    })

}

Connector.prototype.create = function(model,object) {
    var lab = this;
    if (!model || typeof model != 'string' || !models[model]) {
        throw new Error("model " + model +  " not found");
    }
    if(!object.id) {
        object.id = $.uuid();
    }
    return lab.queryRunner.save(model,object).then(record=> {
        return removeIvalidKey(record)
    })
}

Connector.prototype.delete = async function(model, object) {
    if (!model || typeof model != 'string' || !models[model]) {
        throw new Error("model " + model +  " not found");
    }
    var lab = this;
    let conditions = [];
    Object.keys(object).forEach(key=> {
        conditions.push(` ${key} = ? `)
    })
    let result = await lab.query(model,{
        conditions,
        params: Object.values(object)
    })
    let ids = [];
    result.list.forEach(item=> {
        ids.push(item.id);
    })

    if(ids.length) {
        return lab.queryRunner.delete(model,ids);
    } else {
        return;
    }
} 

module.exports = Connector;




